﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace HB.Base
{
    /// <summary>
    /// 高精度定时器
    /// </summary>
    public class TriggerTimer
    {
        /// <summary>
        /// 是否忽略错误
        /// </summary>
        /// <param name="ignoreException"></param>
        public TriggerTimer(bool ignoreException = true)
        {
            this.IgnoreException = ignoreException;
        }
        Stopwatch sw = new Stopwatch();
        bool IgnoreException { get; set; }
        /// <summary>
        /// 是否正在运行中
        /// </summary>
        public bool IsRunning = false;
        /// <summary>
        /// 执行次数
        /// </summary>
        long execTime = 0;
        /// <summary>
        /// 实际执行频率
        /// </summary>
        public double SpeedHZ => execTime * 1000 / sw.ElapsedMilliseconds;
        /// <summary>
        /// 停止运行
        /// </summary>
        public void Stop()
        {
            IsRunning = false;
        }

        /// <summary>
        /// 开始定时任务
        /// </summary>
        /// <param name="hz">频率</param>
        /// <param name="action"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public Task Start(double hz, Action<TriggerTimer, long> action)
        {
            if (IsRunning) { throw new Exception("Alread Run"); }
            return Task.Factory.StartNew(() =>
            {
                var delay = 1000.0 / hz;
                double nextTime = 0;
                execTime = 0;
                IsRunning = true;
                sw.Restart();
                while (IsRunning)
                {
                    SpinWait.SpinUntil(() => sw.ElapsedMilliseconds >= nextTime);
                    nextTime += delay;
                    execTime += 1;
                    try
                    {
                        action.Invoke(this, execTime);
                    }
                    catch (Exception ex)
                    {
                        if(!IgnoreException)
                        {
                            throw ex;
                        }
                    }
                }
                sw.Stop();
            });
        }
    }
}
